import { type Plugin } from 'vite'
import { createHash } from 'crypto'
import { readFileSync, writeFileSync } from 'fs'
import { resolve } from 'path'

interface SriPluginOptions {
  algorithms?: string[]
  includePatterns?: RegExp[]
  excludePatterns?: RegExp[]
}

interface AssetInfo {
  fileName: string
  source: string | Uint8Array
  integrity: string
}

export function viteSriPlugin(options: SriPluginOptions = {}): Plugin {
  const {
    algorithms = ['sha384'],
    includePatterns = [/\.(js|css)$/],
    excludePatterns = []
  } = options

  let assetsMap = new Map<string, AssetInfo>()
  let outputDir = ''

  function generateIntegrity(content: string | Uint8Array, algorithms: string[]): string {
    const hashes = algorithms.map(algorithm => {
      const hash = createHash(algorithm)
      hash.update(content)
      return `${algorithm}-${hash.digest('base64')}`
    })
    return hashes.join(' ')
  }

  function shouldIncludeFile(fileName: string): boolean {
    // 检查是否匹配包含模式
    const matchesInclude = includePatterns.some(pattern => pattern.test(fileName))
    if (!matchesInclude) return false

    // 检查是否匹配排除模式
    const matchesExclude = excludePatterns.some(pattern => pattern.test(fileName))
    return !matchesExclude
  }

  function updateHtmlWithSri(htmlPath: string) {
    try {
      let htmlContent = readFileSync(htmlPath, 'utf-8')
      let updated = false

      // 更新script标签
      htmlContent = htmlContent.replace(
        /<script([^>]*)\s+src="([^"]+)"([^>]*)>/g,
        (match: string, before: string, src: string, after: string) => {
          // 移除开头的斜杠以匹配assets映射
          const fileName = src.startsWith('/assets/') ? src.slice(1) : src.startsWith('./assets/') ? src.slice(2) : src
          const asset = assetsMap.get(fileName)
          
          if (asset && !match.includes('integrity=')) {
            updated = true
            // 检查是否已经有crossorigin属性
            const hasCrossorigin = match.includes('crossorigin')
            const crossoriginAttr = hasCrossorigin ? '' : ' crossorigin="anonymous"'
            return `<script${before} src="${src}"${after} integrity="${asset.integrity}"${crossoriginAttr}>`
          }
          return match
        }
      )

      // 更新link标签（CSS和modulepreload）
      htmlContent = htmlContent.replace(
        /<link([^>]*)\s+href="([^"]+)"([^>]*)>/g,
        (match: string, before: string, href: string, after: string) => {
          // 处理CSS文件和modulepreload文件
          const isStylesheet = match.includes('rel="stylesheet"')
          const isModulepreload = match.includes('rel="modulepreload"')
          
          if (!isStylesheet && !isModulepreload) {
            return match
          }
          
          // 移除开头的斜杠以匹配assets映射
          let fileName = href.startsWith('/assets/') ? href.slice(1) : href.startsWith('./assets/') ? href.slice(2) : href
          
          // 如果href直接以/开头且不包含assets，则添加assets前缀
          if (href.startsWith('/') && !href.startsWith('/assets/') && !href.startsWith('./assets/')) {
            fileName = `assets${href}`
          }
          
          const asset = assetsMap.get(fileName)
          
          if (asset && !match.includes('integrity=')) {
            updated = true
            // 检查是否已经有crossorigin属性
            const hasCrossorigin = match.includes('crossorigin')
            const crossoriginAttr = hasCrossorigin ? '' : ' crossorigin="anonymous"'
            return `<link${before} href="${href}"${after} integrity="${asset.integrity}"${crossoriginAttr}>`
          }
          return match
        }
      )

      if (updated) {
        writeFileSync(htmlPath, htmlContent, 'utf-8')
        // eslint-disable-next-line no-console
        console.log(`✅ SRI hashes added to ${htmlPath}`)
      }
    } catch (error) {
      // eslint-disable-next-line no-console
      console.error(`❌ Error updating HTML file ${htmlPath}:`, error)
    }
  }

  return {
    name: 'vite-plugin-sri',
    apply: 'build',
    
    configResolved(config) {
      outputDir = config.build.outDir || 'dist'
    },

    generateBundle(_options, bundle) {
      // 收集所有需要SRI的资源文件名
      Object.entries(bundle).forEach(([fileName, _chunk]) => {
        if (shouldIncludeFile(fileName)) {
          // 先记录文件名，稍后在writeBundle中读取实际文件内容生成哈希
          assetsMap.set(fileName, {
            fileName,
            source: '', // 占位符，稍后填充
            integrity: '' // 占位符，稍后填充
          })
        }
      })
    },

    writeBundle() {
      // 在文件写入后，读取实际文件内容生成SRI哈希
      assetsMap.forEach((asset, fileName) => {
        try {
          const filePath = resolve(outputDir, fileName)
          const fileContent = readFileSync(filePath)
          const integrity = generateIntegrity(fileContent, algorithms)
          
          // 更新asset信息
          asset.source = fileContent
          asset.integrity = integrity
          
          // eslint-disable-next-line no-console
          console.log(`🔒 Generated SRI for ${fileName}: ${integrity}`)
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error(`❌ Error reading file ${fileName}:`, error)
        }
      })

      // 在所有文件写入完成后，更新HTML文件
      const htmlPath = resolve(outputDir, 'index.html')
      updateHtmlWithSri(htmlPath)

      // 生成SRI映射文件（可选，用于调试）
      const sriMapPath = resolve(outputDir, 'sri-map.json')
      const sriMap = Object.fromEntries(
        Array.from(assetsMap.entries()).map(([fileName, asset]) => [
          fileName,
          {
            integrity: asset.integrity,
            algorithms
          }
        ])
      )
      
      writeFileSync(sriMapPath, JSON.stringify(sriMap, null, 2), 'utf-8')
      // eslint-disable-next-line no-console
      console.log(`📋 SRI map saved to ${sriMapPath}`)
      
      // eslint-disable-next-line no-console
      console.log(`🎉 SRI plugin completed! Protected ${assetsMap.size} assets.`)
    }
  }
}